package com.apples.hexviewer2;

import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.util.Pair;
import android.util.Size;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.DialogFragment;

import com.apples.hexviewer2.databinding.ActivityTwoBinding;
import com.apples.hexviewer2.models.FileData;
import com.apples.hexviewer2.models.LineEntry;
import com.apples.hexviewer2.utils.SysHelper;
import com.apples.hexviewer2.utils.UIHelper;
import com.rustamg.filedialogs.FileDialog;
import com.rustamg.filedialogs.OpenFileDialog;
import com.rustamg.filedialogs.SaveFileDialog;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * hex两个文件差异比较 编辑器
 *
 * @author WangKaiFang
 * @date 2024/08/28
 */
public class TwoActivity extends AppCompatActivity implements FileDialog.OnFileSelectedListener {

    private ActivityTwoBinding binding;
    private long lastTime;
    private FileData fileData1;
    private FileData fileData2;
    private AlertDialog mDialog;
    protected TextView mTextView;
    private String progressText;
    protected long mTotalSize;
    protected long mCurrentSize;
    private volatile boolean mCancel;
    private ArrayList<LineEntry> listData1;
    private ArrayList<LineEntry> listData2;
    private ExecutorService executorService;
    private Handler handler;
    private Runnable updateRunnable;
    private int showMaxLines;
    private int maxLength;
    private int curLineNum;
    private int[] hLight;
    public boolean alertShowing;
    private long lCTime;
    private boolean isHexChanged, isHexChanged2;
    private int lastEntryLen;

    @Override
    protected void onDestroy() {
        super.onDestroy();

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityTwoBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        initData();
    }

    public int countSeparators(String input, String separator) {
        if (input == null || input.isEmpty()) {
            return 0;
        }
        return input.split(separator, -1).length - 1;
    }

    private void initData() {
        listData1 = new ArrayList<>();
        listData2 = new ArrayList<>();
        executorService = Executors.newSingleThreadExecutor();
        handler = new Handler(Looper.getMainLooper());
        initCustomMenu();
        binding.btnSelectFile.setOnClickListener(btnClick);
        binding.btnSelectFile3.setOnClickListener(btnClick);
        binding.btnLoadFile.setOnClickListener(btnClick);
        binding.llRightCtr1.setOnClickListener(btnClick);
        binding.llHexAsciiCtr.setOnClickListener(btnClick);
        binding.llHexAsciiCtr3.setOnClickListener(btnClick);
        binding.btnLoadUp.setOnClickListener(btnClick);
        binding.btnLoadDown.setOnClickListener(btnClick);
        binding.btnDiffUp.setOnClickListener(btnClick);
        binding.btnDiffDown.setOnClickListener(btnClick);
        binding.btnSaveChange.setOnClickListener(btnClick);
        binding.btnSaveOverwrite.setOnClickListener(btnClick);
        binding.btnSaveAs.setOnClickListener(btnClick);
        binding.btnSaveOverwrite3.setOnClickListener(btnClick);
        binding.btnSaveAs3.setOnClickListener(btnClick);
    }

    private final View.OnClickListener btnClick = view -> {
        if (SystemClock.uptimeMillis() - lastTime < 100) return;
        lastTime = SystemClock.uptimeMillis();
        FunActivity.hideSoftKeyboard(this);
        binding.etHexText.clearFocus();
        binding.etHexText3.clearFocus();
        if (view == binding.btnSelectFile) {
            startActivity(1);
        } else if (view == binding.btnSelectFile3) {
            startActivity(2);
        } else if (view == binding.btnLoadFile) {
            startLoadingFile();
        } else if (view == binding.btnLoadUp) {
            if (curLineNum == 0) return;
            if (changeAlert((dialogInterface, i2) -> {
                curLineNum = Math.max(curLineNum - showMaxLines / 2, 0);
                load1$2Down();
            }) == 0) {
                curLineNum = Math.max(curLineNum - showMaxLines / 2, 0);
                load1$2Down();
            }
        } else if (view == binding.btnLoadDown) {
            if (curLineNum + showMaxLines >= listData1.size()) return;
            int i1 = showMaxLines / 2;
            if (changeAlert((dialogInterface, i2) -> {
                curLineNum = Math.min(curLineNum + i1, listData1.size() - i1);
                load1$2Down();
            }) == 0) {
                curLineNum = Math.min(curLineNum + i1, listData1.size() - i1);
                load1$2Down();
            }
        } else if (view == binding.btnDiffUp) {
            int tmp = curLineNum;
            for (int i = curLineNum - 1; i >= 0; i--) {
                LineEntry entry1 = listData1.get(i);
                LineEntry entry2 = listData2.get(i);
                byte[] raw1 = entry1.getRaw();
                byte[] raw2 = entry2.getRaw();
                if (!Arrays.equals(raw1, raw2)) {
                    curLineNum = i - 1;
                    break;
                }
            }
            if (tmp != curLineNum)
                findDiffDown(1);
        } else if (view == binding.btnDiffDown) {
            findDiffDown(showMaxLines);
        } else if (view == binding.btnSaveChange) {
            Pair<Integer, Pair<Integer, Integer>> p = isChanged();
            if (p.first == 1) {
                holdHexContent();
            } else if (p.first == 2) {
                UIHelper.showErrorDialog(this, getString(R.string.error_title),
                        p.second.first + String.format(">>%0" + maxLength + "X  ",
                                p.second.second * 16) + getString(R.string.label_tip_msg28));
            }
        } else if (view == binding.btnSaveOverwrite) {
            if (isHexChanged) {
                save2File1(fileData1.getUri());
            } else {
                FunActivity.showTip(this, getString(R.string.label_tip_msg15));
            }
        } else if (view == binding.btnSaveAs) {
            saveAsFile(1);
        } else if (view == binding.btnSaveOverwrite3) {
            if (isHexChanged2) {
                save2File2(fileData2.getUri());
            } else {
                FunActivity.showTip(this, getString(R.string.label_tip_msg15));
            }
        } else if (view == binding.btnSaveAs3) {
            saveAsFile(2);
        }
    };

    private void saveAsFile(int f12) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        String xx = preferences.getString("hex_viewer_open_path", null);
        SaveFileDialog dialog = new SaveFileDialog();
        Bundle args = new Bundle();
        args.putInt("sArg", f12);
        if (xx != null && !xx.isEmpty())
            args.putSerializable(FileDialog.START_DIRECTORY, new File(xx));
        dialog.setArguments(args);
        dialog.setOldFileName(fileData1.getName());
        dialog.setStyle(DialogFragment.STYLE_NO_TITLE, R.style.Theme_HexViewerLibrary);
        dialog.show(getSupportFragmentManager(), SaveFileDialog.class.getName());
    }

    private void save2File1(Uri uri) {
        if (uri == null) return;
        mDialog = new AlertDialog.Builder(this).create();
        mDialog.setCancelable(false);
        final View v = getLayoutInflater().inflate(R.layout.progress_dialog_hex, null);
        mTextView = v.findViewById(R.id.text);
        progressText = getString(R.string.saving) + " ";
        mTextView.setText(progressText);
        mCancel = false;
        v.findViewById(R.id.cancel).setOnClickListener(view -> mCancel = true);
        mDialog.setView(v);
        mDialog.show();
        executorService.execute(() -> {
            try {
                mCurrentSize = 0;
                mTotalSize = listData1.size();
                ParcelFileDescriptor mFdInput = getContentResolver()
                        .openFileDescriptor(uri, "rw");
                if (mFdInput != null) {
                    FileOutputStream outputStream = new FileOutputStream(mFdInput.getFileDescriptor());
                    ArrayList<Byte> bytes = new ArrayList<>();
                    for (int i = 0; i < listData1.size(); i++) {
                        LineEntry lineEntry = listData1.get(i);
                        byte[] bytes1 = lineEntry.getRaw();
                        for (byte b : bytes1) {
                            bytes.add(b);
                        }
                        mCurrentSize = i + 1;
                        if (mCurrentSize % 640 == 0) {
                            byte[] bytes2 = FunActivity.convertListToByteArray(bytes);
                            outputStream.write(bytes2);
                            bytes.clear();
                            if (updateRunnable == null) {
                                updateRunnable = () -> {
                                    onProgressUpdate(false);
                                    updateRunnable = null;
                                };
                                handler.postDelayed(updateRunnable, 36);
                            }
                            if (mCancel) break;
                        }
                    }
                    if (!bytes.isEmpty()) {
                        byte[] bytes1 = FunActivity.convertListToByteArray(bytes);
                        outputStream.write(bytes1);
                    }
                    bytes.clear();
                    outputStream.close();
                    mFdInput.close();
                    handler.post(() -> {
                        mDialog.dismiss();
                        if (!mCancel) {
                            String s = new File(Objects.requireNonNull(fileData1.getUri().getPath()))
                                    .getAbsolutePath();
                            binding.tvName.setText(s);
                        }
                        isHexChanged = false;
                    });
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    private void save2File2(Uri uri) {
        if (uri == null) return;
        mDialog = new AlertDialog.Builder(this).create();
        mDialog.setCancelable(false);
        final View v = getLayoutInflater().inflate(R.layout.progress_dialog_hex, null);
        mTextView = v.findViewById(R.id.text);
        progressText = getString(R.string.saving) + " ";
        mTextView.setText(progressText);
        mCancel = false;
        v.findViewById(R.id.cancel).setOnClickListener(view -> mCancel = true);
        mDialog.setView(v);
        mDialog.show();
        executorService.execute(() -> {
            try {
                mCurrentSize = 0;
                mTotalSize = listData2.size();
                ParcelFileDescriptor mFdInput = getContentResolver()
                        .openFileDescriptor(uri, "rw");
                if (mFdInput != null) {
                    FileOutputStream outputStream = new FileOutputStream(mFdInput.getFileDescriptor());
                    ArrayList<Byte> bytes = new ArrayList<>();
                    for (int i = 0; i < listData2.size(); i++) {
                        LineEntry lineEntry = listData2.get(i);
                        byte[] bytes1 = lineEntry.getRaw();
                        for (byte b : bytes1) {
                            bytes.add(b);
                        }
                        mCurrentSize = i + 1;
                        if (mCurrentSize % 640 == 0) {
                            byte[] bytes2 = FunActivity.convertListToByteArray(bytes);
                            outputStream.write(bytes2);
                            bytes.clear();
                            if (updateRunnable == null) {
                                updateRunnable = () -> {
                                    onProgressUpdate(false);
                                    updateRunnable = null;
                                };
                                handler.postDelayed(updateRunnable, 36);
                            }
                            if (mCancel) break;
                        }
                    }
                    if (!bytes.isEmpty()) {
                        byte[] bytes1 = FunActivity.convertListToByteArray(bytes);
                        outputStream.write(bytes1);
                    }
                    bytes.clear();
                    outputStream.close();
                    mFdInput.close();
                    handler.post(() -> {
                        mDialog.dismiss();
                        if (!mCancel) {
                            String s = new File(Objects.requireNonNull(fileData2.getUri().getPath()))
                                    .getAbsolutePath();
                            binding.tvName3.setText(s);
                        }
                        isHexChanged2 = false;
                    });
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    private void load1$2Down() {
        int esize = Math.min(curLineNum + showMaxLines, listData1.size());
        for (int i = curLineNum; i < esize; i++) {
            LineEntry entry1 = listData1.get(i);
            LineEntry entry2 = listData2.get(i);
            byte[] raw1 = entry1.getRaw();
            byte[] raw2 = entry2.getRaw();
            if (!Arrays.equals(raw1, raw2)) {
                List<Size> idxHex = new ArrayList<>();
                List<Size> idxTxt = new ArrayList<>();
                for (int i1 = 0; i1 < raw1.length; i1++) {
                    if (raw1[i1] != raw2[i1]) {
                        int sidxH = i1 * 3;
                        int eidxH = sidxH + 3;
                        idxHex.add(new Size(sidxH, eidxH));
                        int eidxT = i1 + 1;
                        idxTxt.add(new Size(i1, eidxT));
                    }
                }
                entry1.mHighHex = idxHex;
                entry1.mHighText = idxTxt;
                entry1.highLineNum = i - curLineNum;
                entry2.mHighHex = idxHex;
                entry2.mHighText = idxTxt;
                entry2.highLineNum = i - curLineNum;
            }
        }
        fillHex2ET();
    }

    private void findDiffDown(int o) {
        int esize = listData1.size();
        boolean tmp = true;
        int tmp2 = curLineNum;
        int tmp3 = curLineNum + o;
        if (tmp3 >= esize)
            tmp3 = curLineNum;
        for (int i = tmp3; i < esize; i++) {
            LineEntry entry1 = listData1.get(i);
            LineEntry entry2 = listData2.get(i);
            byte[] raw1 = entry1.getRaw();
            byte[] raw2 = entry2.getRaw();
            if (!Arrays.equals(raw1, raw2)) {
                List<Size> idxHex = new ArrayList<>();
                List<Size> idxTxt = new ArrayList<>();
                for (int i1 = 0; i1 < raw1.length; i1++) {
                    if (raw1[i1] != raw2[i1]) {
                        int sidxH = i1 * 3;
                        int eidxH = sidxH + 3;
                        idxHex.add(new Size(sidxH, eidxH));
                        int eidxT = i1 + 1;
                        idxTxt.add(new Size(i1, eidxT));
                    }
                }
                if (tmp) {
                    tmp = false;
                    curLineNum = i;
                    esize = Math.min(curLineNum + showMaxLines, listData1.size());
                }
                entry1.mHighHex = idxHex;
                entry1.mHighText = idxTxt;
                entry1.highLineNum = i - curLineNum;
                entry2.mHighHex = idxHex;
                entry2.mHighText = idxTxt;
                entry2.highLineNum = i - curLineNum;
            }
        }
        if (tmp2 != curLineNum)
            fillHex2ET();
    }

    private int changeAlert(DialogInterface.OnClickListener listener) {
        Pair<Integer, Pair<Integer, Integer>> p = isChanged();
        if (p.first == 1) {
            new AlertDialog.Builder(this)
                    .setCancelable(false)
                    .setMessage(getString(R.string.label_tip_msg1))
                    .setPositiveButton(getString(R.string.label_tip_msg2),
                            (dialogInterface, i2) -> holdHexContent())
                    .setNegativeButton(getString(R.string.label_tip_msg3), listener)
                    .setNeutralButton(getString(R.string.label_button_cancel), null)
                    .create().show();
        } else if (p.first == 2) {
            UIHelper.showErrorDialog(this, getString(R.string.error_title),
                    p.second.first + String.format(">>%0" + maxLength + "X  ",
                            p.second.second * 16) + getString(R.string.label_tip_msg28));
        }
        return p.first;
    }

    private Pair<Integer, Pair<Integer, Integer>> isChanged() {
        String s1 = binding.etHexText.getText().toString();
        String s2 = binding.etHexText3.getText().toString();
        if (s1.isEmpty()) {
            return new Pair<>(2, new Pair<>(1, 0));
        }
        if (s2.isEmpty()) {
            return new Pair<>(2, new Pair<>(2, 0));
        }
        final int i316 = 2 * 16;
        Pair<Integer, Pair<Integer, Integer>> p = new Pair<>(0, new Pair<>(0, -1));
        String[] hexStrings = s1.split("\n");
        for (int i = 0; i < hexStrings.length; i++) {
            int i1 = i + curLineNum;
            String hexString = hexStrings[i];
            String validate = hexString.replace(" ", "")
                    .toUpperCase(Locale.ENGLISH);
            if (i1 == listData1.size() - 1) {
                if (validate.length() != lastEntryLen * 2) {
                    p = new Pair<>(2, new Pair<>(1, i1));
                    break;
                }
            } else {
                if (validate.length() != i316) {
                    p = new Pair<>(2, new Pair<>(1, i1));
                    break;
                }
            }
            String hex = listData1.get(i1).getHex();
            if (!hex.equals(hexString)) {
                p = new Pair<>(1, new Pair<>(1, i1));
                break;
            }
        }
        hexStrings = binding.etHexText3.getText().toString().split("\n");
        for (int i = 0; i < hexStrings.length; i++) {
            int i1 = i + curLineNum;
            String hexString = hexStrings[i];
            String validate = hexString.replace(" ", "")
                    .toUpperCase(Locale.ENGLISH);
            if (i1 == listData1.size() - 1) {
                if (validate.length() != lastEntryLen * 2) {
                    p = new Pair<>(2, new Pair<>(2, i1));
                    break;
                }
            } else {
                if (validate.length() != i316) {
                    p = new Pair<>(2, new Pair<>(2, i1));
                    break;
                }
            }
            String hex = listData2.get(i1).getHex();
            if (!hex.equals(hexString)) {
                p = new Pair<>(1, new Pair<>(2, i1));
                break;
            }
        }
        return p;
    }

    private void holdHexContent() {
        if (SystemClock.uptimeMillis() - lCTime > 300) {
            lCTime = SystemClock.uptimeMillis();
            String text = binding.etHexText.getText().toString();
            String[] hexStrings = text.split("\n");
            int slineNum2 = curLineNum;
            boolean icge = false;
            for (String string : hexStrings) {
                String validate = string.replace(" ", "")
                        .toUpperCase(Locale.ENGLISH);
                if (!SysHelper.isValidHexLine(validate)) {
                    FunActivity.showTip(this, String.format("%0" + maxLength + "X  ",
                            slineNum2 * 16) + getString(R.string.error_invalid_value));
                    return;
                }
                if (!icge) {
                    String hex = listData1.get(slineNum2).getHex();
                    if (!hex.equals(string)) {
                        icge = true;
                    }
                }
                slineNum2++;
            }
            if (icge) {
                ArrayList<LineEntry> list = new ArrayList<>();
                for (String string : hexStrings) {
                    String validate = string.replace(" ", "")
                            .toUpperCase(Locale.ENGLISH);
                    if (validate.isEmpty()) continue;
                    byte[] bytes = SysHelper.hexStringToByteArray(validate);
                    if (bytes.length > 16) {
                        List<byte[]> list1 = SysHelper.splitByteArray(bytes, 16);
                        for (byte[] bytes1 : list1) {
                            list.add(new LineEntry(bytes1));
                        }
                    } else {
                        list.add(new LineEntry(bytes));
                    }
                }
                listData1.subList(curLineNum, Math.min(curLineNum + showMaxLines, listData1.size()))
                        .clear();
                listData1.addAll(curLineNum, list);
                String s = "*" + new File(Objects.requireNonNull(fileData1.getUri().getPath()))
                        .getAbsolutePath();
                binding.tvName.setText(s);
                isHexChanged = true;
            }
        }
        holdHexContent2();
    }

    private void holdHexContent2() {
        String text = binding.etHexText3.getText().toString();
        String[] hexStrings = text.split("\n");
        int slineNum2 = curLineNum;
        boolean icge = false;
        for (String string : hexStrings) {
            String validate = string.replace(" ", "")
                    .toUpperCase(Locale.ENGLISH);
            if (!SysHelper.isValidHexLine(validate)) {
                FunActivity.showTip(this, String.format("%0" + maxLength + "X  ",
                        slineNum2 * 16) + getString(R.string.error_invalid_value));
                return;
            }
            if (!icge) {
                String hex = listData2.get(slineNum2).getHex();
                if (!hex.equals(string)) {
                    icge = true;
                }
            }
            slineNum2++;
        }
        if (icge) {
            ArrayList<LineEntry> list = new ArrayList<>();
            for (String string : hexStrings) {
                String validate = string.replace(" ", "")
                        .toUpperCase(Locale.ENGLISH);
                if (validate.isEmpty()) continue;
                byte[] bytes = SysHelper.hexStringToByteArray(validate);
                if (bytes.length > 16) {
                    List<byte[]> list1 = SysHelper.splitByteArray(bytes, 16);
                    for (byte[] bytes1 : list1) {
                        list.add(new LineEntry(bytes1));
                    }
                } else {
                    list.add(new LineEntry(bytes));
                }
            }
            listData2.subList(curLineNum, Math.min(curLineNum + showMaxLines, listData2.size()))
                    .clear();
            listData2.addAll(curLineNum, list);
            String s = "*" + new File(Objects.requireNonNull(fileData2.getUri().getPath()))
                    .getAbsolutePath();
            binding.tvName3.setText(s);
            isHexChanged2 = true;
        }
        load1$2Down();
    }

    private void startLoadingFile() {
        String s1 = binding.tvName.getText().toString();
        if (TextUtils.isEmpty(s1)) {
            FunActivity.showTip(this, getString(R.string.label_tip_msg24, 1));
            return;
        }
        String s2 = binding.tvName3.getText().toString();
        if (TextUtils.isEmpty(s2)) {
            FunActivity.showTip(this, getString(R.string.label_tip_msg24, 2));
            return;
        }
        if (TextUtils.equals(s1, s2)) {
            FunActivity.showTip(this, getString(R.string.label_tip_msg27));
            return;
        }
        fileData1 = new FileData(this, Uri.fromFile(new File(s1)));
        if (fileData1.isNotFound()) {
            FunActivity.showTip(this, getString(R.string.label_tip_msg25, 1));
            return;
        }
        fileData2 = new FileData(this, Uri.fromFile(new File(s2)));
        if (fileData2.isNotFound()) {
            FunActivity.showTip(this, getString(R.string.label_tip_msg25, 2));
            return;
        }
        if (fileData1.getSize() != fileData2.getSize()) {
            FunActivity.showTip(this, getString(R.string.label_tip_msg26));
            return;
        }
        loadFileAlert();
    }

    private void loadFileAlert() {
        mDialog = new AlertDialog.Builder(this).create();
        mDialog.setCancelable(false);
        final View v = getLayoutInflater().inflate(R.layout.progress_dialog_hex, null);
        mTextView = v.findViewById(R.id.text);
        mCancel = false;
        v.findViewById(R.id.cancel).setOnClickListener(view -> mCancel = true);
        mDialog.setView(v);
        mDialog.show();
        loadFile1();
    }

    private void loadFile1() {
        progressText = getString(R.string.loading) + " (1) ";
        mTextView.setText(progressText);
        executorService.execute(() -> {
            try {
                mCurrentSize = 0;
                mTotalSize = fileData1.getSize();
                ParcelFileDescriptor mFdInput = getContentResolver()
                        .openFileDescriptor(fileData1.getUri(), "r");
                if (mFdInput != null) {
                    FileInputStream inputStream = new FileInputStream(mFdInput.getFileDescriptor());
                    byte[] buffer = new byte[1024 * 10];
                    int read;
                    while ((read = inputStream.read(buffer)) != -1) {
                        int offset = 0;
                        while (offset < read) {
                            int remainingBytes = read - offset;
                            int chunkSize = Math.min(16, remainingBytes);
                            byte[] chunk = new byte[chunkSize];
                            System.arraycopy(buffer, offset, chunk, 0, chunkSize);
                            listData1.add(new LineEntry(chunk));
                            offset += chunkSize;
                            if (mCancel) break;
                        }
                        mCurrentSize += read;
                        if (updateRunnable == null) {
                            updateRunnable = () -> {
                                onProgressUpdate(true);
                                updateRunnable = null;
                            };
                            handler.postDelayed(updateRunnable, 36);
                        }
                        if (mCancel) break;
                    }
                    inputStream.close();
                    mFdInput.close();
                    handler.post(() -> {
                        mDialog.dismiss();
                        if (!mCancel) {
                            loadFileDone(1);
                        }
                    });
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    private void loadFile2() {
        progressText = getString(R.string.loading) + " (2) ";
        mTextView.setText(progressText);
        executorService.execute(() -> {
            try {
                mCurrentSize = 0;
                mTotalSize = fileData2.getSize();
                ParcelFileDescriptor mFdInput = getContentResolver()
                        .openFileDescriptor(fileData2.getUri(), "r");
                if (mFdInput != null) {
                    FileInputStream inputStream = new FileInputStream(mFdInput.getFileDescriptor());
                    byte[] buffer = new byte[1024 * 10];
                    int read;
                    while ((read = inputStream.read(buffer)) != -1) {
                        int offset = 0;
                        while (offset < read) {
                            int remainingBytes = read - offset;
                            int chunkSize = Math.min(16, remainingBytes);
                            byte[] chunk = new byte[chunkSize];
                            System.arraycopy(buffer, offset, chunk, 0, chunkSize);
                            listData2.add(new LineEntry(chunk));
                            offset += chunkSize;
                            if (mCancel) break;
                        }
                        mCurrentSize += read;
                        if (updateRunnable == null) {
                            updateRunnable = () -> {
                                onProgressUpdate(true);
                                updateRunnable = null;
                            };
                            handler.postDelayed(updateRunnable, 36);
                        }
                        if (mCancel) break;
                    }
                    inputStream.close();
                    mFdInput.close();
                    handler.post(() -> {
                        mDialog.dismiss();
                        if (!mCancel) {
                            loadFileDone(2);
                        }
                    });
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    public void onProgressUpdate(boolean iLoad) {
        String text = progressText;
        if (iLoad) {
            text += SysHelper.sizeToHuman(this, mCurrentSize) + " / "
                    + SysHelper.sizeToHuman(this, mTotalSize);
        } else {
            text += String.format(Locale.ENGLISH, "%.2f%%",
                    (mCurrentSize * 1.0 / mTotalSize) * 100);
        }
        mTextView.setText(text);
    }

    private void loadFileDone(int f12) {
        if (f12 == 1) {
            loadFile2();
        } else if (f12 == 2) {
            maxLength = String.format(Locale.ENGLISH, "%X", fileData1.getSize()).length();
            int lh = binding.tvNumberOrder.getLineHeight();
            int h = binding.tvNumberOrder.getHeight();
            showMaxLines = h / lh;
            byte[] idx = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
            binding.etHexText2.setText(LineEntry.lbthex(idx));
            binding.etHexText4.setText(LineEntry.lbthex(idx));
            binding.flLoadFileCtr.setVisibility(View.GONE);
            binding.flSf.setVisibility(View.GONE);
            binding.flSf3.setVisibility(View.GONE);
            LineEntry lastEntry = listData1.get(listData1.size() - 1);
            lastEntryLen = lastEntry.getRaw().length;
            int esize = listData1.size();
            boolean tmp = true;
            for (int i = curLineNum; i < esize; i++) {
                LineEntry entry1 = listData1.get(i);
                LineEntry entry2 = listData2.get(i);
                byte[] raw1 = entry1.getRaw();
                byte[] raw2 = entry2.getRaw();
                if (!Arrays.equals(raw1, raw2)) {
                    List<Size> idxHex = new ArrayList<>();
                    List<Size> idxTxt = new ArrayList<>();
                    for (int i1 = 0; i1 < raw1.length; i1++) {
                        if (raw1[i1] != raw2[i1]) {
                            int sidxH = i1 * 3;
                            int eidxH = sidxH + 3;
                            idxHex.add(new Size(sidxH, eidxH));
                            int eidxT = i1 + 1;
                            idxTxt.add(new Size(i1, eidxT));
                        }
                    }
                    if (tmp) {
                        tmp = false;
                        curLineNum = i;
                        esize = Math.min(curLineNum + showMaxLines, listData1.size());
                    }
                    entry1.mHighHex = idxHex;
                    entry1.mHighText = idxTxt;
                    entry1.highLineNum = i - curLineNum;
                    entry2.mHighHex = idxHex;
                    entry2.mHighText = idxTxt;
                    entry2.highLineNum = i - curLineNum;
                }
            }
            if (tmp) {
                FunActivity.showTip2(this, getString(R.string.label_tip_msg29));
            }
            fillHex2ET();
        }
    }

    private void fillHex2ET() {
        StringBuilder order = new StringBuilder();
        StringBuilder hex = new StringBuilder();
        StringBuilder ascii = new StringBuilder();
        StringBuilder order3 = new StringBuilder();
        StringBuilder hex3 = new StringBuilder();
        StringBuilder ascii3 = new StringBuilder();
        final int i1 = Math.min(curLineNum + showMaxLines, listData1.size());
        boolean isHigh = false;
        for (int i = curLineNum; i < i1; i++) {
            LineEntry lineEntry = listData1.get(i);
            hex.append("\n");
            hex.append(lineEntry.getHex());
            ascii.append("\n");
            ascii.append(lineEntry.getAscii());
            order.append("\n");
            order.append(String.format(Locale.ENGLISH, "%0" + maxLength + "X",
                    i * 16L));
            LineEntry lineEntry3 = listData2.get(i);
            hex3.append("\n");
            hex3.append(lineEntry3.getHex());
            ascii3.append("\n");
            ascii3.append(lineEntry3.getAscii());
            order3.append("\n");
            order3.append(String.format(Locale.ENGLISH, "%0" + maxLength + "X",
                    i * 16L));

            if (!isHigh && lineEntry.highLineNum != -1)
                isHigh = true;
        }
        order.deleteCharAt(0);
        hex.deleteCharAt(0);
        ascii.deleteCharAt(0);
        if (order.indexOf("\n") == -1)
            order.append('\n');
        if (hex.indexOf("\n") == -1)
            hex.append('\n');
        if (ascii.indexOf("\n") == -1)
            ascii.append('\n');
        binding.tvNumberOrder.setText(order);
        binding.etHexText.setText(hex);
        binding.tvAsciiText.setText(ascii);
        binding.tvNumberOrder.post(() -> {
            int w = binding.tvNumberOrder.getWidth();
            int w2 = binding.tvNumberOrder2.getWidth();
            if (w2 != w)
                binding.tvNumberOrder2.setWidth(w);
        });
        order3.deleteCharAt(0);
        hex3.deleteCharAt(0);
        ascii3.deleteCharAt(0);
        if (order3.indexOf("\n") == -1)
            order3.append('\n');
        if (hex3.indexOf("\n") == -1)
            hex3.append('\n');
        if (ascii3.indexOf("\n") == -1)
            ascii3.append('\n');
        binding.tvNumberOrder3.setText(order3);
        binding.etHexText3.setText(hex3);
        binding.tvAsciiText3.setText(ascii3);
        binding.tvNumberOrder3.post(() -> {
            int w = binding.tvNumberOrder3.getWidth();
            int w2 = binding.tvNumberOrder4.getWidth();
            if (w2 != w)
                binding.tvNumberOrder4.setWidth(w);
        });
        if (isHigh) {
            binding.etHexText.post(() -> {
                for (int i = curLineNum; i < i1; i++) {
                    LineEntry lineEntry = listData1.get(i);
                    int i3 = lineEntry.highLineNum;
                    if (i3 == -1) continue;
                    List<Size> idxHex = lineEntry.mHighHex;
                    List<Size> idxTxt = lineEntry.mHighText;
                    for (int i2 = 0; i2 < idxHex.size(); i2++) {
                        Size size1 = idxHex.get(i2);
                        int sidxH = size1.getWidth();
                        int eidxH = size1.getHeight();
                        FunActivity.selectText(binding.etHexText, i3 * 3 * 16 + i3 + sidxH,
                                i3 * 3 * 16 + i3 + eidxH, 1);
                        Size size21 = idxTxt.get(i2);
                        int sidxT = size21.getWidth();
                        int eidxT = size21.getHeight();
                        FunActivity.selectText(binding.tvAsciiText, i3 * 16 + i3 + sidxT,
                                i3 * 16 + i3 + eidxT, 1);
                    }
                }
            });
            binding.etHexText3.post(() -> {
                for (int i = curLineNum; i < i1; i++) {
                    LineEntry lineEntry = listData2.get(i);
                    int i3 = lineEntry.highLineNum;
                    if (i3 == -1) continue;
                    List<Size> idxHex = lineEntry.mHighHex;
                    List<Size> idxTxt = lineEntry.mHighText;
                    for (int i2 = 0; i2 < idxHex.size(); i2++) {
                        Size size1 = idxHex.get(i2);
                        int sidxH = size1.getWidth();
                        int eidxH = size1.getHeight();
                        FunActivity.selectText(binding.etHexText3, i3 * 3 * 16 + i3 + sidxH,
                                i3 * 3 * 16 + i3 + eidxH, 1);
                        Size size21 = idxTxt.get(i2);
                        int sidxT = size21.getWidth();
                        int eidxT = size21.getHeight();
                        FunActivity.selectText(binding.tvAsciiText3, i3 * 16 + i3 + sidxT,
                                i3 * 16 + i3 + eidxT, 1);
                    }
                }
            });
        }
    }

    public void startActivity(int f12) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        String xx = preferences.getString("hex_viewer_open_path", null);
        OpenFileDialog dialog = new OpenFileDialog();
        Bundle args = new Bundle();
        args.putInt("sArg", f12);
        args.putInt("oFn", 2);
        if (xx != null && !xx.isEmpty())
            args.putSerializable(FileDialog.START_DIRECTORY, new File(xx));
        dialog.setArguments(args);
        dialog.setStyle(DialogFragment.STYLE_NO_TITLE, R.style.Theme_HexViewerLibrary);
        dialog.show(this.getSupportFragmentManager(), OpenFileDialog.class.getName());
    }

    @Override
    public void onFileSelected(FileDialog dialog, File file) {
        if (file == null) return;
        int type = dialog.getType();
        Uri data = Uri.fromFile(file);
        if (type == 1) {
            int sr = dialog.getsArg();
            if (sr == 1) {
                binding.tvName.setText(file.getAbsolutePath());
            } else if (sr == 2) {
                binding.tvName3.setText(file.getAbsolutePath());
            }
        } else if (type == 2) {
            int sr = dialog.getsArg();
            if (sr == 1) {
                save2File1(data);
            } else if (sr == 2) {
                save2File2(data);
            }
        }
    }

    @Override
    public void onBackPressed() {
        if (isHexChanged) {
            new AlertDialog.Builder(this)
                    .setCancelable(false)
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(R.string.action_close_title)
                    .setMessage(String.format(getString(R.string.confirm_save), fileData1.getName()))
                    .setPositiveButton(R.string.yes, (dialog, which) -> {
                        save2File1(fileData1.getUri());
                        dialog.dismiss();
                    })
                    .setNegativeButton(R.string.no, (dialog, which) -> finish())
                    .setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss())
                    .show();
            return;
        }
        if (isHexChanged2) {
            new AlertDialog.Builder(this)
                    .setCancelable(false)
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(R.string.action_close_title)
                    .setMessage(String.format(getString(R.string.confirm_save), fileData2.getName()))
                    .setPositiveButton(R.string.yes, (dialog, which) -> {
                        save2File2(fileData2.getUri());
                        dialog.dismiss();
                    })
                    .setNegativeButton(R.string.no, (dialog, which) -> finish())
                    .setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss())
                    .show();
            return;
        }
        super.onBackPressed();
    }

    private void initCustomMenu() {
        binding.etHexText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                // 添加自定义的菜单项
                menu.add(0, 1, 0, getString(R.string.menu_crc16_hex));
                menu.add(0, 2, 1, getString(R.string.menu_sum_hex));
                return true;
            }

            private long ptm;

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                // 可以在这里更新菜单项
                MenuItem shareItem = menu.findItem(android.R.id.shareText);
                if (shareItem != null)
                    menu.removeItem(android.R.id.shareText);
                long upTimed = SystemClock.uptimeMillis();
                if (upTimed - ptm > 100) {
                    ptm = upTimed;
                    EditText editText = binding.etHexText;
                    int start = editText.getSelectionStart();
                    int end = editText.getSelectionEnd();
                    if (start != end) {
                        String sText = editText.getText().subSequence(0, start + 1).toString();
                        String sText2 = sText.replace(" ", "");
                        int sText3 = countSeparators(sText2, "\n");
                        sText2 = sText2.replace("\n", "");
                        int i2 = sText2.length() / 2 + sText3;
                        String selText = editText.getText().subSequence(start, end).toString();
                        String selText2 = selText.replace(" ", "");
                        int selText3 = countSeparators(selText2, "\n");
                        selText2 = selText2.replace("\n", "");
                        int i3 = i2 + selText2.length() / 2 + selText3;
                        FunActivity.selectText(binding.tvAsciiText, i2, i3);
                        hLight = new int[]{start, end};
                    }
                }
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case 1:
                        // 处理“CRC16计算”菜单项的点击事件
                        showCRCHTMLDialog();
                        return true;
                    case 2:
                        // 处理“和计算”菜单项的点击事件
                        actionSUM();
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                if (alertShowing) return;
                FunActivity.clearSelection(binding.tvAsciiText);
                binding.tvAsciiText.clearFocus();
            }
        });
        binding.tvAsciiText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
                menu.add(0, 1, 0, getString(R.string.menu_crc16_hex));
                menu.add(0, 2, 1, getString(R.string.menu_sum_hex));
                return true;
            }

            private long ptm;

            @Override
            public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
                // 可以在这里更新菜单项
                MenuItem shareItem = menu.findItem(android.R.id.shareText);
                if (shareItem != null)
                    menu.removeItem(android.R.id.shareText);
                long upTimed = SystemClock.uptimeMillis();
                if (upTimed - ptm > 100) {
                    ptm = upTimed;
                    TextView textView = binding.tvAsciiText;
                    int start = textView.getSelectionStart();
                    int end = textView.getSelectionEnd();
                    if (start != end) {
                        String sText = textView.getText().subSequence(0, start).toString();
                        int sText2 = countSeparators(sText, "\n");
                        sText = sText.replace("\n", "");
                        int i2 = sText.length() * 3 + sText2;
                        String selText = textView.getText().subSequence(start, end).toString();
                        int selText3 = countSeparators(selText, "\n");
                        selText = selText.replace("\n", "");
                        int i3 = i2 + selText.length() * 3 + selText3;
                        FunActivity.selectText(binding.etHexText, i2, i3);
                        hLight = new int[]{i2, i3};
                    }
                }
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode actionMode, MenuItem item) {
                switch (item.getItemId()) {
                    case 1:
                        // 处理“CRC16计算”菜单项的点击事件
                        showCRCHTMLDialog();
                        return true;
                    case 2:
                        // 处理“和计算”菜单项的点击事件
                        actionSUM();
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public void onDestroyActionMode(ActionMode actionMode) {
                if (alertShowing) return;
                FunActivity.clearSelection(binding.etHexText);
                binding.etHexText.clearFocus();
            }
        });
        binding.etHexText3.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                // 添加自定义的菜单项
                menu.add(0, 1, 0, getString(R.string.menu_crc16_hex));
                menu.add(0, 2, 1, getString(R.string.menu_sum_hex));
                return true;
            }

            private long ptm;

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                // 可以在这里更新菜单项
                MenuItem shareItem = menu.findItem(android.R.id.shareText);
                if (shareItem != null)
                    menu.removeItem(android.R.id.shareText);
                long upTimed = SystemClock.uptimeMillis();
                if (upTimed - ptm > 100) {
                    ptm = upTimed;
                    EditText editText = binding.etHexText3;
                    int start = editText.getSelectionStart();
                    int end = editText.getSelectionEnd();
                    if (start != end) {
                        String sText = editText.getText().subSequence(0, start + 1).toString();
                        String sText2 = sText.replace(" ", "");
                        int sText3 = countSeparators(sText2, "\n");
                        sText2 = sText2.replace("\n", "");
                        int i2 = sText2.length() / 2 + sText3;
                        String selText = editText.getText().subSequence(start, end).toString();
                        String selText2 = selText.replace(" ", "");
                        int selText3 = countSeparators(selText2, "\n");
                        selText2 = selText2.replace("\n", "");
                        int i3 = i2 + selText2.length() / 2 + selText3;
                        FunActivity.selectText(binding.tvAsciiText3, i2, i3);
                        hLight = new int[]{start, end};
                    }
                }
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case 1:
                        // 处理“CRC16计算”菜单项的点击事件
                        showCRCHTMLDialog();
                        return true;
                    case 2:
                        // 处理“和计算”菜单项的点击事件
                        actionSUM();
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                if (alertShowing) return;
                FunActivity.clearSelection(binding.tvAsciiText3);
                binding.tvAsciiText3.clearFocus();
            }
        });
        binding.tvAsciiText3.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
                menu.add(0, 1, 0, getString(R.string.menu_crc16_hex));
                menu.add(0, 2, 1, getString(R.string.menu_sum_hex));
                return true;
            }

            private long ptm;

            @Override
            public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
                // 可以在这里更新菜单项
                MenuItem shareItem = menu.findItem(android.R.id.shareText);
                if (shareItem != null)
                    menu.removeItem(android.R.id.shareText);
                long upTimed = SystemClock.uptimeMillis();
                if (upTimed - ptm > 100) {
                    ptm = upTimed;
                    TextView textView = binding.tvAsciiText3;
                    int start = textView.getSelectionStart();
                    int end = textView.getSelectionEnd();
                    if (start != end) {
                        String sText = textView.getText().subSequence(0, start).toString();
                        int sText2 = countSeparators(sText, "\n");
                        sText = sText.replace("\n", "");
                        int i2 = sText.length() * 3 + sText2;
                        String selText = textView.getText().subSequence(start, end).toString();
                        int selText3 = countSeparators(selText, "\n");
                        selText = selText.replace("\n", "");
                        int i3 = i2 + selText.length() * 3 + selText3;
                        FunActivity.selectText(binding.etHexText3, i2, i3);
                        hLight = new int[]{i2, i3};
                    }
                }
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode actionMode, MenuItem item) {
                switch (item.getItemId()) {
                    case 1:
                        // 处理“CRC16计算”菜单项的点击事件
                        showCRCHTMLDialog();
                        return true;
                    case 2:
                        // 处理“和计算”菜单项的点击事件
                        actionSUM();
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public void onDestroyActionMode(ActionMode actionMode) {
                if (alertShowing) return;
                FunActivity.clearSelection(binding.etHexText3);
                binding.etHexText3.clearFocus();
            }
        });
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void showCRCHTMLDialog() {
        if (SystemClock.uptimeMillis() - lCTime < 333 || hLight == null) return;
        lCTime = SystemClock.uptimeMillis();
        int start = hLight[0];
        int end = hLight[1];
        if (start == 0 || end == 0)
            return;
        String s = binding.etHexText.getText().toString().substring(start, end);
        String s1 = s.replace(" ", "").replace("\n", "");
        if (TextUtils.isEmpty(s1)) return;
        if (s1.length() % 2 != 0) {
            FunActivity.showTip(this, getString(R.string.error_invalid_value));
            return;
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setCancelable(false);
        View view = LayoutInflater.from(this).inflate(R.layout.dialog_hex_crc_webview,
                null);
        WebView wb = view.findViewById(R.id.wb_hex_crc);
        View pb = view.findViewById(R.id.progressBar);
        String filePath = "file:///android_asset/crccalc.com/index.htm?paramName=" + s1;
        WebSettings myWebSettings = wb.getSettings();
        myWebSettings.setJavaScriptEnabled(true);//设置支持javascript
        myWebSettings.setUseWideViewPort(true);//将图片调整到适合webView大小
        myWebSettings.setLoadWithOverviewMode(true);//缩放至屏幕大小
        myWebSettings.setDomStorageEnabled(true);//设置DOM缓存
        myWebSettings.setCacheMode(android.webkit.WebSettings.LOAD_NO_CACHE);//设置去缓存
        myWebSettings.setDatabaseEnabled(true);
        myWebSettings.setAllowFileAccess(true);
        myWebSettings.setAllowContentAccess(true);
        myWebSettings.setAllowUniversalAccessFromFileURLs(true);
        wb.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                pb.postDelayed(() -> pb.setVisibility(View.INVISIBLE), 69);
            }
        });
        wb.loadUrl(filePath);
        builder.setView(view);
        builder.setPositiveButton(R.string.tip_alert_ok_hex, null);
        AlertDialog dialog = builder.create();
        dialog.setOnDismissListener(dialogInterface -> alertShowing = false);
        dialog.show();
        alertShowing = true;
    }

    private void actionSUM() {
        if (SystemClock.uptimeMillis() - lCTime < 333 || hLight == null) return;
        lCTime = SystemClock.uptimeMillis();
        int start = hLight[0];
        int end = hLight[1];
        if (start == 0 || end == 0)
            return;
        String s = binding.etHexText.getText().toString().substring(start, end);
        String s1 = s.replace(" ", "").replace("\n", "");
        if (TextUtils.isEmpty(s1)) return;
        if (s1.length() % 2 != 0) {
            FunActivity.showTip(this, getString(R.string.error_invalid_value));
            return;
        }
        String[] s2 = splitHexString(s1);
        int sum = 0;
        StringBuilder sbr = new StringBuilder();
        for (String hex : s2) {
            if (TextUtils.isEmpty(hex)) continue;
            sum += Integer.parseInt(hex, 16);
        }
        String result = Integer.toHexString(sum).toUpperCase();
        if (result.length() % 2 == 1)
            result = "0" + result;
        sbr.append(getString(R.string.label_tip_msg14)).append(result);
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setCancelable(false)
                .setMessage(sbr)
                .setPositiveButton(R.string.tip_alert_ok_hex, null)
                .create();
        dialog.setOnDismissListener(dialogInterface -> alertShowing = false);
        dialog.show();
        alertShowing = true;
        TextView textView = dialog.findViewById(android.R.id.message);
        TextView okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
        okButton.setTextSize(20f);
        okButton.setTextColor(getColor(R.color.colorBlueSelectedHex));
        if (textView != null) {
            textView.setTextSize(24f);
            textView.post(() -> {
                String text = sbr.toString();
                SpannableString spannableString = new SpannableString(text);
                int start2 = text.indexOf(":");
                spannableString.setSpan(new ForegroundColorSpan(Color.WHITE),
                        0, start2 + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                spannableString.setSpan(new ForegroundColorSpan(Color.RED),
                        start2 + 1, sbr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                textView.setText(spannableString);
            });
        }
    }

    public String[] splitHexString(String hexString) {
        int length = hexString.length();
        String[] result = new String[length / 2];
        for (int i = 0; i < length; i += 2) {
            result[i / 2] = hexString.substring(i, i + 2);
        }
        return result;
    }
}